/*
 * @Author lihui
 * @Email  451514854@qq.com
 * @Desc pull . loadmore and scroll - plugins
 * @Created by lihui on 2018/3/20.
 * */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
      (global.VuPull = factory());
}(this, function () {
  /* @Desc 空Fun*/
  function nop() {
  }

  /* @Desc 检测对象是否是dom */
  var isDOMEle = (typeof HTMLElement === 'object') ?
    function (obj) {
      return obj instanceof HTMLElement;
    } :
    function (obj) {
      return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';
    };

  /*@Desc 检测浏览器厂商开头的transition属性支持情况，并返回支持的属性*/
  function whichTransitionEvent() {
    var t,
      el = document.createElement("fakeelement");
    var transitions = {
      "transition": "",
      "OTransition": "o",
      "MozTransition": "moz",
      "WebkitTransition": "webkit",
      "MsTransition": "ms"
    };
    for (t in transitions) {
      if (el.style[t] !== undefined) {
        return transitions[t];
      }
    }
  }

  // @Desc 浏览器兼容性 获取头部
  var broHead = whichTransitionEvent();
  var transform = !broHead ? 'transform' : (broHead + 'Transform');
  var transition = !broHead ? 'transition' : (broHead + 'Transition');


  /*@Desc class 主函数 */
  function VuPull() {

    // @Desc el-下拉对象
    this.el = null;

    // @Desc scrollEl-滚动对象
    this.scrollEl = null;

    // @Desc默认配置
    this.defaults = {
      topCloseElMove: false,   // 如果设置为true，对象下拉的时候将不会移动
      bottomCloseElMove: false,   // 如果设置为true，对象上拉拉的时候将不会移动
      isTopRefresh: false, //是否开启下拉刷新
      isBottomRefresh: false, // 是否开启上拉加载
      isTopBounce: false, // 是否开启下拉弹性效果
      isBottomBounce: false, // 是否开启上拉弹性效果
      slideResistance: 2, // 滑动的阻力
      topTriggerHeight: 50, //下拉的有效距离
      bottomTriggerHeight: 50, // 上拉的有效距离
      scrollTriggerValue: 10 // 设置滚动到底部多少触发刷新
    }

    // @Desc 默认的监听下拉状态变更
    this.onTopStatusChange = nop;

    // @Desc 监听下拉事件
    this.onTop = nop;

    // @Desc 监听上拉状态变更
    this.onBottomStatusChange = nop;

    // @Desc 监听上拉事件
    this.onBottom = nop;

    // @Desc 监听下拉结束
    this.onPullEnd = nop;

    // @Desc 回调滚动到底部时间
    this.onScrollBottom = nop;

    // @desc 操作数据
    this.scrollState = null
    this.firstSlideDirection = null; //第一次滑动的方向
    this.bottomReached = false; // 是否滚动到底部
    this.diff = 0;    // 当前元素距离
    this.beforeDiff = 0; // 上次元素的距离
    this.startY = 0;
    this.startX = 0;
    this.moveY = 0;
    this.scrollTop = 0;
  }


  var vup = VuPull.prototype;

  /*@Desc 初始化数据*/
  vup.init = function (el, opt) {

    var that = this;
    var def = this.defaults;

    opt = opt || {};

    // 检查操作对象是否为dom
    if (!isDOMEle(el)) {
      console.error('[Vup.init]-eln不是dom对象-' + el)
      return
    }

    this.el = el;

    this.scrollEl = el.querySelector('.pull-scroll-container')

    // 开启下拉刷新， 默认开启弹性效果
    if (opt.isTopRefresh === true) {
      opt.isTopBounce = true
    }

    // 开启上拉加载， 默认开启弹性效果
    if (opt.isBottomRefresh === true) {
      opt.isBottomBounce = true
    }

    // 合并配置
    for (var d in def) {
      if (opt.hasOwnProperty(d)) {
        def[d] = opt[d]
      }
    }


    /* @Desc 监听下拉状态变更*/
    var _pullState = this.pullState

    Object.defineProperty(this, 'pullState', {
      configurable: true,
      set: function (val) {
        if (val === _pullState) {
          return
        }
        _pullState = val
        that.onTopStatusChange.call(that)
      },
      get: function () {
        return _pullState
      }
    });

    /* @Desc 监听上拉拉状态变更*/
    var _bottomState = this.bottomState
    Object.defineProperty(this, 'bottomState', {
      configurable: true,
      set: function (val) {
        if (val === _bottomState) {
          return
        }
        _bottomState = val
        that.onBottomStatusChange.call(that)
      },
      get: function () {
        return _bottomState
      }
    });

    this.bindEvent()

    console.log('vuu-pull-初始化完成');
  }

  /* @Desc touchstart*/
  vup.handleStart = function (event) {
    this.firstSlideDirection = null;
    this.beforeDiff = this.diff;
    this.startY = event.touches[0].clientY;
    this.startX = event.touches[0].clientX;
    this.scrollTop = this.scrollEl.scrollTop;
    this.bottomReached = this.checkBottomReached();
  }

  /* @Desc touchmove*/
  vup.handleMove = function (event) {
    var that = this
    var def = this.defaults;

    // @Desc 初始化第一次滑动的方向
    this.initFirstSlideDirection(event);

    this.moveY = (event.touches[0].clientY - this.startY) / def.slideResistance + this.beforeDiff;

    // 下拉的时候
    if (this.firstSlideDirection === 'y' && def.isTopBounce && this.moveY > 0 && this.scrollTop === 0) {

      event.preventDefault();
      event.stopPropagation();

      // 避免频繁触发loading状态
      if (this.pullState === 'loading' && this.moveY >= this.diff) {
        return
      }

      this.diff = this.moveY

      if (!def.topCloseElMove) {
        this.el.style[transform] = 'translate3d(0,' + that.diff + 'px,0)';
      }

      if (this.diff >= def.topTriggerHeight && this.pullState !== 'trigger') {
        this.pullState = 'trigger'
      }

      if (this.diff < def.topTriggerHeight) {
        this.pullState = 'pull';
      }

      this.onTop.call(this)
    }

    // 上拉的时候

    if (this.firstSlideDirection === 'y' && def.isBottomBounce && this.moveY < 0 && this.bottomReached) {
      event.preventDefault();
      event.stopPropagation();
      // 避免频繁触发loading状态
      if (this.bottomState === 'loading' && this.moveY <= this.diff) {
        return
      }

      this.diff = this.moveY

      if (!def.bottomCloseElMove) {
        this.el.style[transform] = 'translate3d(0,' + that.diff + 'px,0)';
      }

      if (Math.abs(this.diff) >= def.bottomTriggerHeight && this.bottomState !== 'trigger') {
        this.bottomState = 'trigger'
      }

      if (Math.abs(this.diff) < def.bottomTriggerHeight) {
        this.bottomState = 'pull';
      }

      this.onBottom.call(this)
    }

  }

  /* @Desc touchend*/
  vup.handleEnd = function (event) {
    var def = this.defaults;
    event.stopPropagation();
    if (this.diff >= 0) {
      if (def.isTopBounce) {
        if (this.pullState === 'trigger') {
          if (def.isTopRefresh) {
            this.pullState = 'loading'
            this.aiMoveTop(this.defaults.topTriggerHeight, 200, 'top')
          } else {
            this.closeLoadTop()
          }
        } else {
          this.closeLoadTop()
        }
      }
    } else {
      if (def.isBottomBounce) {
        if (this.bottomState === 'trigger') {
          if (def.isBottomRefresh) {
            this.bottomState = 'loading'
            this.aiMoveTop(-this.defaults.bottomTriggerHeight, 200, 'bottom')
          } else {
            this.closeLoadBottom()
          }
        } else {
          this.closeLoadBottom()
        }
      }
    }
  }

  /* @Desc 监听滚动条滚动到底部 */
  vup.handleScroll = function () {
    if (this.checkBottomReached(this.defaults.scrollTriggerValue)) {
      if (this.scrollState) {
        return
      }
      this.scrollState = true
      this.onScrollBottom.call(this)
    } else {
      this.scrollState = null
    }
  }

  /* @Desc 检测是否到底部 */
  vup.checkBottomReached = function (dent) {
    return this.scrollEl.scrollTop + this.scrollEl.offsetHeight + (dent || 1) >= this.scrollEl.scrollHeight;
  }

  /* @Desc initFirstSlideDirection 初始化第一次滑动的方向*/
  vup.initFirstSlideDirection = function (event) {
    if (!this.firstSlideDirection) {
      var tc = event.touches[0];
      this.firstSlideDirection = Math.abs(tc.clientX - this.startX) - Math.abs(tc.clientY - this.startY) >= 0 ? 'x' : 'y'
    }
  }

  /* @Desc close*/
  vup.closeLoadTop = function () {
    this.pullState = 'normal'
    this.aiMoveTop(0, 200, 'top')
  }

  /* @Desc close*/
  vup.closeLoadBottom = function () {
    this.bottomState = 'normal'
    this.aiMoveTop(0, 200, 'bottom')
  }

  /* @Desc aiMoveTo*/
  vup.aiMoveTop = function (y, duration, type) {
    var that =this
    var def = this.defaults
    this.diff = y
    if ((type === 'top' && !def.topCloseElMove) || (type === 'bottom' && !def.bottomCloseElMove)) {
      this.el.style[transform] = 'translate3d(0,' + this.diff + 'px,0)';
    }
    this.el.style[transition] = duration + 'ms';
    setTimeout(function() {
      that.el.style.transition = '';
    }, duration);
  }

  /* @Desc bind*/
  vup.bindEvent = function () {
    this.el.addEventListener('touchstart', this.handleStart.bind(this), false)
    this.el.addEventListener('touchmove', this.handleMove.bind(this), false)
    this.el.addEventListener('touchend', this.handleEnd.bind(this), false)
    this.scrollEl.addEventListener('scroll', this.handleScroll.bind(this), false)
  }

  /* @Desc remove*/
  vup.removeEvent = function () {
    this.el.removeEventListener('touchstart', this.handleStart.bind(this), false)
    this.el.removeEventListener('touchmove', this.handleMove.bind(this), false)
    this.el.removeEventListener('touchend', this.handleEnd.bind(this), false)
    this.scrollEl.removeEventListener('scroll', this.handleScroll.bind(this), false)
  }

  return VuPull
}))
